package com.wendal.java.vfilesystem;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;

public class VFileSystem implements IFileSystem {

	private VFileDataZone fileDataZone;

	private VFileIndex fileIndex;

	private int blockSize;
	
	public VFileSystem(File dataFile, int size, int blockSize)
			throws FileNotFoundException {
		if (blockSize < 1 || blockSize > size)
			throw new IllegalArgumentException();
		this.blockSize = blockSize;
		this.fileIndex = new VFileIndex(size / blockSize);
		this.fileDataZone = new VFileDataZone(dataFile, blockSize);
	}

	public void add(String key, Serializable serializable)
			throws IOException {
		if (key == null || serializable == null)
			return;
		remove(key);
		byte[] fileData = object2ByteArray(serializable);
		byte[][] cb = cutupByteArray(fileData);
		int[] blocks = this.fileIndex.getAvailableBlock(cb.length);
		for (int i = 0; i < blocks.length; i++) {
			this.fileDataZone.writeBlock(blocks[i], cb[i]);
		}
		this.fileIndex.maskFlag(blocks, true);
		VFile vFile = new VFile();
		vFile.setBlocks(blocks);
		vFile.setFileSize(fileData.length);
		this.fileIndex.addVFile(key, vFile);
	}

	public void remove(String key) {
		if (key == null)
			return;
		this.fileIndex.remove(key);
	}

	public void reset() throws IOException {
		this.fileIndex.reset();
		this.fileDataZone.reset();
	}

	public Serializable read(String key) throws IOException,
			ClassNotFoundException {
		VFile file = this.fileIndex.getVFile(key);
		if (file != null) {
			int[] blocks = file.getBlocks();
			byte[] tmpfileData = new byte[blocks.length * blockSize];
			int pointer = 0;
			for (int i = 0; i < blocks.length; i++) {
				byte[] falg = this.fileDataZone.readBlock(blocks[i]);
				for (int j = 0; j < falg.length; j++) {
					tmpfileData[j + pointer] = falg[j];
				}
				pointer += this.blockSize;
			}
			byte[] fileData = Arrays.copyOf(tmpfileData, file.getFileSize());
			ByteArrayInputStream bais = new ByteArrayInputStream(fileData);
			ObjectInputStream ois = new ObjectInputStream(bais);
			return (Serializable)ois.readObject();
		}else
			return null;
	}

	public VFileIndex getCurrentVFileIndex(){
		return fileIndex.clone();
	}
	
	private byte[][] cutupByteArray(byte[] data) {
		int bs = data.length / this.blockSize + 1;
		byte[][] cb = new byte[bs][];
		for (int i = 0; i < bs; i++) {
			byte[] falg = Arrays.copyOfRange(data, i * this.blockSize, (i + 1) * this.blockSize);
			cb[i] = falg;
		}
		return cb;
	}
	
	private byte [] object2ByteArray(Serializable serializable) throws IOException{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(serializable);
		oos.flush();
		oos.close();
		return baos.toByteArray();
	}
}